本篇將解釋 mix project 的基本概念,以及各種函式的呼叫方式。
mix 是 elixir 內建的多功能指令。它可以用來新建專案、安裝管理函式庫及可執行套件、執行測試、生成文件等等。絕大多數 elixir 的套件都會使用 mix 的各種功能,來讓大家的操作方式更加一致。
我們先試試用 mix 建立一個專案叫 hello_elixir,打開 shell,輸入以下指令
$ mix new hello_elixir
$ cd hello_elixir
可以看到 mix 新增了一個叫 hello_elixir
的資料夾,並在裡面生成了許多檔案。我們剛剛已經用 cd
切換到這個目錄裡了。接下來我們用編輯器打開 mix.exs
,它的內容長這樣:
defmodule HelloElixir.MixProject do
use Mix.Project
def project do
[
app: :hello_elixir,
version: "0.1.0",
elixir: "~> 1.6-rc",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
def application do
#...
end
defp deps do
#...
end
end
其中 project
函式裡定義了這個專案的基本資料,像是名稱,版本及需要的 elixir 版本等等。而底下的 application
及 deps
分別是這個執行期需要載入的模組,及這個專案依賴的外部函式庫。不過這部份不是今天的重點,在後面的章節會提到什麼時候需要改動這個檔案。
依照 mix 專案的慣例,主要的程式碼都會放在 lib
資料夾底下。打開 lib/hello_elixir
,我們可以看到已經生成了一些樣版的程式碼。最上方的模組名稱,是 HelloElixir
,就是把專案名稱 CamelCase 化。
我們先在這個檔案裡 hello
函式的底下加入一個新的函式,讓它看起來像是這樣:
def hello do
:world
end
def foo do
"Foo the #{hello()}"
end
def bar(list) do
Enum.max(list) + 1
end
在 foo
函式裡我們示範了同一個模組下,我們可以直接呼叫其它的函式,不需要加上模組名稱。而若是想呼叫其它的模組的函式時,就像在 bar
函式中的示範,就必須加上模組名稱來呼叫: Enum.max(list)
由於 elixir 的內建工具鏈設計的相當完整,所以他們之間可以很緊密的互動。我們先回到 shell ,在 hello_elixir
目錄下打開之前用過的 iex
互動介面,但是這次的指令有一點點不一樣:
$ iex -S mix
iex
的 -S
參數可以讓互動式介面找到路徑裡的 script 並執行,這樣一來我們就把互動式介面就類似掛載進當前目錄下的 mix 專案裡了。來試著呼叫剛定義好的函式吧:
iex(1)> HelloWorld.foo
"Foo the world"
Note: 當你輸入 Hel
的時後,按下鍵盤上的 [TAB],會有自動補完的功能。如果可以補完的選項不只一個,按兩下會出現所有可用的補完項目。
接著我們來多做一個模組。在 lib
底下新增一個資料夾叫 helpers
,並在其下新增一個檔案,叫baz_helper.ex
。在檔案裡我們寫入這些內容:
defmodule HelloElixir.Helpers.BazHelper do
def min(list) do
Enum.min(list)
end
def zoom do
"Zoom!!"
end
defp hoo do
"This is a private function"
end
end
Elixir 的模組命名慣例,除了會用大寫 CamelCase 之外,會是 專案名.路徑.檔案名
,像是這裡示範的 HelloElixir.Helpers.BazHelper
alias
縮寫如果想在其它模組中呼叫 BazHelper
的函式,除了每次都使用全名之外,可以用 alias 模組全名
來減少需要打的字,後續的呼叫只要輸入模組名稱的最後一部份即可。
defmodule HelloElixir do
alias HelloElixir.Helpers.BazHelper
def try_alias do
BazHelper.baz
end
end
alias... as:
來改用其它名字如果有多個重覆的名稱,或是你覺得原先的名稱不好打,可以用 as:
選項改取其它名稱。但是一般來說非必要不會使用這個技巧,因為其它讀程式的人,得要翻回檔案最上方才知道這個模組原先的名字是什麼。
defmodule HelloElixir do
alias HelloElixir.Helpers.BazHelper, as: Baz
do try_as do
Baz.baz
end
end
alias
多個模組alias HelloElixir.Helper.{BazHelper, BooHelper, ZooHelper}
import
如果你發現這兩個模組緊密相關,常常會用到該模組的函式,不想每次都多打字時,可以使用 import
,只要函式名稱沒有衝突,就可以當做同一個模組的函式來呼叫。
defmodule HelloElixir do
import HelloElixir.Helpers.BazHelper
def try_import do
hoo()
end
end
所以其實你在寫 elixir 時,可以自由的使用四則運算、 elem/2
、def/2
等函式,就是因為每個 elixir 模組,都預設 import
了 Kernel
模組底下的所有函式。
如果你只想要部份的函式,你可以使用 only
選項。
import HelloElixir.Helpers.BazHelper, only: [baz: 1]
import
接受一個 keyword list,鍵是想要匯入的函式名稱,值是參數個數,可以一次匯入多個函式。
另外也可以改用 except
選項,去除不想要的函式。如果你想要覆寫 Kernel
裡已經有的函式,你就可以這樣做:
defmodule HelloElixir do
import Kernel, except: [elem: 2]
def elem(list, _index) do
Enum.at(list, Enum.random(1..length(list)))
end
end
require
Elixir 中提供了強大的元編程 (meta programming) 能力,所以一般函式做不到的事,可以撰寫 macro 來處理。在這裡我們只要先知道 macro 類似功能更強大的函式就可以了。而想要使用某個模式下的所有 defmacro
定義的話,要先用 require 模組名稱
。例如 elixir 內建的 Interger
模組:
iex> require Integer
iex> Integer.is_odd(3)
true
use
除了繼承之外,有些程式語言還提供了不同的機制,來將已經寫好的函式合成到其它檔案中,例如 Ruby 就有 mixin 讓你把函式插入目前的類別裡。而在 elixir 裡,則提供了 use 模組名稱
的語法。當你在某個模組裡,寫了 use Aaa
時,會呼叫 Aaa
模組下的 __using__
這個 macro。裡面可以自由定義該模組會在 use
它的的模組裡做什麼事。
目前我們只要知道 use
可以對你的模組做出某些改變,讓它有一些特殊的功能 (通常就是合成一些函式進來)。簡單的例子可以看看我寫的 pipe_to,它提供了 use PipeTo.Override
,讓你的 |>
運算子可以選擇要 pipe 到不同的參數位置。這種能力就是運用這個機制做出來的。更詳細的部份,就留待之後有篇幅再來講解了。
進行到一半,我們終於把 Elixir 的語法部份大致交待完了。只差 macro、protocol、behaviour (沒有拼錯字) 及 with 等比較進階的功能。下一篇,我們就要開始來探索 Phoenix 這個框架了。
Happy Hacking! 明天見。